package com.lagou.server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.*;

public class Bootstrap {

    /**
     * 定义socket监听的端口号
     * @param args
     */
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws IOException {
        //加载配置文件
        loadServer();

        //定义一个线程池
        int corePoolSize = 10;
        int maxinumPoolSize = 50;
        long keepAliveTime = 180L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maxinumPoolSize,keepAliveTime, unit,workQueue,threadFactory,handler);

        //完成Minicat 1.0版本
        //需求：浏览器请求http://localhost:8080，返回一个固定的字符串到页面 "hello Minicat"

        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("====>Minicat start on port:"+ port);

        /*while (true){
            Socket socket = serverSocket.accept();
            OutputStream outputStream = socket.getOutputStream();
            String data =  "hello  Minicat!";
            String responseTest = HttpProtocalUtil.getHttpHeader200(data.getBytes().length) + data;
            outputStream.write(responseTest.getBytes());
            socket.close();
        }*/

        /**
         * 完成完成Minicat 2.0版本
         * 需求：封装Request和Response对象，返回html静态资源文件
         */
        /*while (true){
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            //从输入流中获取请求信息

            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            response.outputHtml(request.getUrl());

            socket.close();
        }*/

        /**
         * 完成完成Minicat 3.0版本
         * 需求：可以请求资源（servlet）
         */
        /*while (true){
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            //从输入流中获取请求信息

            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            HttpServlet httpServlet = servletMap.get(request.getUrl());
            if(httpServlet == null){
                response.outputHtml(request.getUrl());
            }else {
                try {
                    httpServlet.service(request,response);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }



            socket.close();
        }*/

        //多线程改造（不使用线程池）
        /*while (true){
            Socket socket = serverSocket.accept();

            RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
            requestProcessor.start();
        }*/

        //多线程改造（使用线程池）
        while (true){
            Socket socket = serverSocket.accept();

            RequestProcessor requestProcessor = new RequestProcessor(socket,appServletMap);
            //requestProcessor.start();
            threadPoolExecutor.execute(requestProcessor);
        }
    }

    private Map<String,Map<String,HttpServlet>> appServletMap = new HashMap<>();

    /**
     * 加载server.xml文件
     */
    private void loadServer(){
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();

        try {
            Document read = saxReader.read(resourceAsStream);
            Element rootElement = read.getRootElement();

            Element serviceElement = (Element)rootElement.selectSingleNode("Service");
            Element engineElement = (Element)serviceElement.selectSingleNode("Engine");

            //读取connector节点
            Element connectorElement = (Element)serviceElement.selectSingleNode("Connector");
            //读取host节点
            Element hostElement = (Element)engineElement.selectSingleNode("Host");

            port = Integer.valueOf(connectorElement.attributeValue("port"));

            String appBase = hostElement.attributeValue("appBase");

            //加载webapps
            File file = new File(appBase);

            if(!file.exists() || !file.isDirectory()){
                throw new RuntimeException("file not exists");
            }

            File[] files = file.listFiles();
            Arrays.stream(files).forEach(fileLocal -> {
                if(fileLocal.isDirectory()){
                    //文件夹名
                    String name = fileLocal.getName();
                    Map<String, HttpServlet> servletMap = null;
                    try {
                        servletMap = loadServlet(fileLocal);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    }
                    appServletMap.put(name,servletMap);
                }
            });
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 加载解析web.xml，初始化servlet
     */
    private Map<String,HttpServlet> loadServlet(File file) throws FileNotFoundException, MalformedURLException {
        Map<String,HttpServlet> servletMap = new HashMap<>();

        File[] files = file.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                //加载web.xml
                if (name.equals("web.xml")) {
                    return true;
                }
                return false;
            }
        });
        if(files.length == 0){
            throw new RuntimeException("not find web.xml");
        }

        InputStream resourceAsStream = new FileInputStream(files[0]);
        //InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");

        //获取URL对象
        URL url = file.toURI().toURL();

        //创建URL类加载器
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});

        SAXReader saxReader = new SAXReader();

        try {
            Document read = saxReader.read(resourceAsStream);
            Element rootElement = read.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");

            selectNodes.stream().forEach(element -> {
                Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                String sevletName = servletnameElement.getStringValue();
                Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                String sevletClass = servletclassElement.getStringValue();

                Node sevletMapping = rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + sevletName + "']");
                String urlPattern = sevletMapping.selectSingleNode("url-pattern").getStringValue();

                try {
                    //通过urlClassLoader加载器调用loadClass方法传入类名动态加载class文件并获取class对象:会初始化静态块
                    Class<?> classs = urlClassLoader.loadClass(sevletClass);
                    servletMap.put(urlPattern,(HttpServlet) classs.newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            });

        } catch (DocumentException e) {
            e.printStackTrace();
        }

        return servletMap;
    }

    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
